package com.ttjj.entity.navi;

import com.ttjj.entity.map.Position;
import com.ttjj.service.navigation.TerrainAnalyzer;
import lombok.Data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 地形导航
 */
@Data
public class TerrainNaviRoute {
    protected final Position posA;
    protected final Position posB;
    protected final int hash;
    protected boolean reachable;
    protected TerrainStep finalStep;
    protected boolean isRevert;
    private final Object locker = new Object();

    public TerrainNaviRoute(Position posA, Position posB) {
        this.posA = posA;
        this.posB = posB;
        this.hash = getRouteHash(posA, posB);
        this.reset();
    }

    public void reset() {
        this.reachable = !TerrainAnalyzer.isOverlap(this.posA) && !TerrainAnalyzer.isOverlap(this.posB);
        if (this.reachable && this.posA.equals(this.posB)) {
            this.finalStep = new TerrainStep(this.posA, null);
        }

    }

    public void setRoute(TerrainStep finalStep) {
        synchronized(this.locker) {
            this.finalStep = finalStep;
            this.isRevert = this.posA.equals(finalStep.getPos());
        }
    }

    public int getTotalCost() {
        synchronized(this.locker) {
            return this.finalStep != null ? this.finalStep.getTotalCost() : Integer.MAX_VALUE;
        }
    }

    public TerrainStep getAnotherFinalStep(Position endPos) {
        if (!this.finalStep.getPos().equals(endPos)) {
            synchronized(this.locker) {
                List<Position> passBys = this.getPassBys(this.isRevert ? this.posA : this.posB);
                TerrainStep lastStep = new TerrainStep(passBys.get(0), null);

                for(int i = 1; i < passBys.size(); ++i) {
                    lastStep = new TerrainStep(passBys.get(i), lastStep);
                }

                return lastStep;
            }
        } else {
            return this.finalStep;
        }
    }

    public List<Position> getPassBys(Position beginPos) {
        List<Position> result = new ArrayList();
        synchronized(this.locker) {
            for(TerrainStep current = this.finalStep; current != null; current = current.getLastStep()) {
                result.add(current.getPos());
            }

            if (this.posA.equals(beginPos) ^ this.isRevert) {
                Collections.reverse(result);
            }

            return result;
        }
    }

    public Position getNextStepPosition(Position beginPos) {
        synchronized(this.locker) {
            if (this.finalStep != null && this.finalStep.getLastStep() != null) {
                if (!(this.posA.equals(beginPos) ^ this.isRevert)) {
                    return this.finalStep.getLastStep().getPos();
                } else {
                    TerrainStep current;
                    for(current = this.finalStep; current.getLastStep().getLastStep() != null; current = current.getLastStep()) {
                    }

                    return current.getPos();
                }
            } else {
                return null;
            }
        }
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj != null && this.getClass() == obj.getClass()) {
            TerrainNaviRoute other = (TerrainNaviRoute)obj;
            return this.hash == other.hash;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return this.hash;
    }

    public static int getRouteHash(Position posA, Position posB) {
        int hashA = posA.hashCode();
        int hashB = posB.hashCode();
        return hashA <= hashB ? hashA * 448 + hashB : hashB * 448 + hashA;
    }
}
